package com.tcm.utils;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @program: fjx_server_back
 * @description: 用于借助es进行模糊搜索
 * @author: Jason
 * @create: 2023-03-27 19:35
 */
@Component
public class ESUtil {
    @Autowired
    public RestHighLevelClient restHighLevelClient;

    /**
     * @des: 借助es对某表的某字段进行模糊查询，使用前需@Autowired自动注入该工具类
     * @param: pojo 要进行模糊查询的pojo类的class对象
     * @param: field 要模糊查询的表字段名
     * @param: value 要查询的值
     * @return: List<String> 返回主键id，拿到id后再做后续处理(没有status字段的表可能会查出已经删除的记录，建议没有status字段的不做模糊查询)
     * 举例：esUtil.FuzzyQueriesByES(Images.class,"image_name","甘草");
     *      上述表示：要从 tb_images 表中模糊查询值为"甘草"的"image_name"字段
     **/
    public List<String> FuzzyQueriesByES(Class<? extends Object> pojo, String field, String value) {
        /*拿表名*/
        TableName annotation = pojo.getAnnotation(TableName.class);
        String tableName = annotation.value();
        /*拿主键名*/
        String primaryKey = "";
        Field[] declaredFields = pojo.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            TableId tableId = declaredField.getAnnotation(TableId.class);
            if (tableId != null) {
                primaryKey = humpToUnderline(declaredField.getName());
                break;
            }
        }

        /*查询*/
        SearchRequest searchRequest = new SearchRequest(tableName);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(field, value);
        /*查询是否有status*/
        Optional<Field> hasStatus = Arrays.stream(declaredFields).filter(declaredField -> declaredField.getName().equals("status")).findFirst();
        if (hasStatus.isPresent()) {
            TermQueryBuilder status = QueryBuilders.termQuery("status", "0");
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery().must(status).must(matchQueryBuilder);
            searchSourceBuilder.query(boolQuery);
            searchRequest.source(searchSourceBuilder);
        }
        else {   //无status字段
            searchSourceBuilder.query(matchQueryBuilder);
            searchRequest.source(searchSourceBuilder);
        }
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            System.out.println("搜索错误");
            throw new RuntimeException(e);
        }
        SearchHit[] hits = response.getHits().getHits();
        ArrayList<String > ids = new ArrayList<>();
        for (SearchHit hit : hits) {
            Map<String, Object> map = hit.getSourceAsMap();
            ids.add(map.get(primaryKey).toString());
        }
        return ids;
    }

    /***
     * 驼峰命名转为下划线命名
     * @param para
     * 驼峰命名的字符串
     */
    private static String humpToUnderline(String para) {
        String separator = "_";
        para = para.replaceAll("([a-z])([A-Z])", "$1"+separator+"$2").toLowerCase();
        return para;
    }
}
